﻿using System;
using System.ComponentModel;
using Microsoft.WowAddonStudio.FrameXml.Components.Descriptors;
using Microsoft.WowAddonStudio.FrameXml.Components.Descriptors.Filters;
using Microsoft.WowAddonStudio.FrameXml.Serialization;

namespace Microsoft.WowAddonStudio.FrameXml.Components
{
    public partial class FrameXmlComponent<TSerializationObject> : ICustomTypeDescriptor
    {
        private static readonly PropertyDescriptorFilterManager serializationObjectPropertyDescriptorFilterManager = new PropertyDescriptorFilterManager();

        /// <summary>
        /// Initializes the <see cref="FrameXmlComponent&lt;TSerializationObject&gt;"/> class.
        /// </summary>
        static FrameXmlComponent()
        {
            // Initialize property descriptor filter managers
            serializationObjectPropertyDescriptorFilterManager.Add<InheritedPropertyDescriptorFilter>();
            serializationObjectPropertyDescriptorFilterManager.Add<InheritsListPropertyDescriptorFilter>();
            serializationObjectPropertyDescriptorFilterManager.Add<UiTypeEditorPropertyDescriptorFilter>();
        }

        /// <summary>
        /// Returns a collection of custom attributes for this instance of a component.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.AttributeCollection"/> containing the attributes for this object.
        /// </returns>
        public AttributeCollection GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }

        /// <summary>
        /// Returns the class name of this instance of a component.
        /// </summary>
        /// <returns>
        /// The class name of the object, or null if the class does not have a name.
        /// </returns>
        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        /// <summary>
        /// Returns the name of this instance of a component.
        /// </summary>
        /// <returns>
        /// The name of the object, or null if the object does not have a name.
        /// </returns>
        public string GetComponentName()
        {
			if(!string.IsNullOrEmpty(Name))
				return Name;

			return TypeDescriptor.GetClassName(this, true);
        }

        /// <summary>
        /// Returns a type converter for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.TypeConverter"/> that is the converter for this object, or null if there is no <see cref="T:System.ComponentModel.TypeConverter"/> for this object.
        /// </returns>
        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }

        /// <summary>
        /// Returns the default event for this instance of a component.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptor"/> that represents the default event for this object, or null if this object does not have events.
        /// </returns>
        public EventDescriptor GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        /// <summary>
        /// Returns the default property for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that represents the default property for this object, or null if this object does not have properties.
        /// </returns>
        public PropertyDescriptor GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }

        /// <summary>
        /// Returns an editor of the specified type for this instance of a component.
        /// </summary>
        /// <param name="editorBaseType">A <see cref="T:System.Type"/> that represents the editor for this object.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> of the specified type that is the editor for this object, or null if the editor cannot be found.
        /// </returns>
        public object GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        /// <summary>
        /// Returns the events for this instance of a component.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptorCollection"/> that represents the events for this component instance.
        /// </returns>
        public EventDescriptorCollection GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        /// <summary>
        /// Returns the events for this instance of a component using the specified attribute array as a filter.
        /// </summary>
        /// <param name="attributes">An array of type <see cref="T:System.Attribute"/> that is used as a filter.</param>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptorCollection"/> that represents the filtered events for this component instance.
        /// </returns>
        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        /// <summary>
        /// Returns the properties for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> that represents the properties for this component instance.
        /// </returns>
        public PropertyDescriptorCollection GetProperties()
        {
            return this.GetProperties(null);
        }

        /// <summary>
        /// Returns the properties for this instance of a component using the attribute array as a filter.
        /// </summary>
        /// <param name="attributes">An array of type <see cref="T:System.Attribute"/> that is used as a filter.</param>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> that represents the filtered properties for this component instance.
        /// </returns>
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var result = new PropertyDescriptorCollection(null);

            // Get property descriptors from both serialization object and control
            var serializationObjectProperties = TypeDescriptor.GetProperties(this.SerializationObject, attributes);

            // Iterate through the serialization object's property descriptors and add any [Editor] attributes if required
            foreach (PropertyDescriptor propertyDescriptor in serializationObjectProperties)
            {
                if (this.serializationObject is InheritableObject)
                {
                    // Extend serialization object's property with type description provider
                    Serialization.InheritableObject inheritableObject = (Serialization.InheritableObject)serializationObject;
                    FrameXmlTypeDescriptionProviderExtender.Extend(inheritableObject, propertyDescriptor);
                }

                // Run property descriptor through DP filters
                var frameXmlPropertyDescriptor = serializationObjectPropertyDescriptorFilterManager.Filter(this, propertyDescriptor);
                if (frameXmlPropertyDescriptor != null)
                    result.Add(FrameXmlDescriptorFactory.CreateProperty<TSerializationObject>(frameXmlPropertyDescriptor));
            }

            return result;
        }

        /// <summary>
        /// Returns an object that contains the property described by the specified property descriptor.
        /// </summary>
        /// <param name="propertyDescriptor">A <see cref="T:System.ComponentModel.PropertyDescriptor"/> that represents the property whose owner is to be found.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the owner of the specified property.
        /// </returns>
        public object GetPropertyOwner(PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor != null && propertyDescriptor.ComponentType.IsAssignableFrom(this.SerializationObject.GetType()))
                return this.SerializationObject;

            return this;
        }
    }
}
